///////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2000-2003 Intel Corporation
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// * Neither name of Intel Corporation nor the names of its contributors
// may be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
///////////////////////////////////////////////////////////////////////////

#include "xmlbuffer.h"
#include "xmlparser.h"

/*================================================================
*   XmlCopyWithEscape
*
*
*=================================================================*/
void
XmlCopyWithEscape( INOUT XmlBuffer * buf,
                  IN char *p )
{
    int i;
    int plen;

    if ( p == NULL )
        return;

    plen = strlen( p );

    for ( i = 0; i < plen; i++ )
    {
        switch ( p[i] )
        {
        case '<':
            XmlBufferAppendString( buf, "&lt;" );
            break;

        case '>':
            XmlBufferAppendString( buf, "&gt;" );
            break;

        case '&':
            XmlBufferAppendString( buf, "&amp;" );
            break;

        case '\'':
            XmlBufferAppendString( buf, "&apos;" );
            break;

        case '\"':
            XmlBufferAppendString( buf, "&quot;" );
            break;

        default:
            XmlBufferAppend( buf, &p[i] );
        }
    }
}

/*================================================================
*	XmlPrintDomTreeRecursive
*       It is a recursive function to print all the node in a tree.
*       Internal to parser only.
*
*=================================================================*/
void
XmlPrintDomTreeRecursive( IN XmlNode *   nodeptr,
                            IN XmlBuffer * buf )
{
    char *nodeName = NULL;
    char *nodeValue = NULL;
    XmlNode *  child = NULL;
    XmlNode *  sibling = NULL;

    if ( nodeptr != NULL )
    {
        nodeName = ( char * )XmlNodeGetNodeName( nodeptr )?:"";
        nodeValue = XmlNodeGetNodeValue( nodeptr );

        switch ( XmlNodeGetNodeType( nodeptr ) )
        {

        case E_TEXT_NODE:
            XmlCopyWithEscape( buf, nodeValue );
            break;

        case E_CDATA_SECTION_NODE:
            XmlBufferAppendString( buf, nodeValue );
            break;

        case E_PROCESSING_INSTRUCTION_NODE:
            XmlBufferAppendString( buf, "<?" );
            XmlBufferAppendString( buf, nodeName );
            XmlBufferAppendString( buf, " " );
            XmlBufferAppendString( buf, nodeValue );
            XmlBufferAppendString( buf, "?>\n" );
            break;

        case E_DOCUMENT_NODE:
            XmlPrintDomTreeRecursive( XmlNodeGetFirstChild
                                        ( nodeptr ), buf );
            break;

        case E_ATTRIBUTE_NODE:
            XmlBufferAppendString( buf, nodeName );
            XmlBufferAppendString( buf, "=\"" );
            if ( nodeValue != NULL )
            {
                XmlBufferAppendString( buf, nodeValue );
            }
            XmlBufferAppendString( buf, "\"" );
            if ( nodeptr->nextSibling != NULL )
            {
                XmlBufferAppendString( buf, " " );
                XmlPrintDomTreeRecursive( nodeptr->nextSibling, buf );
            }
            break;

        case E_ELEMENT_NODE:
            XmlBufferAppendString( buf, "<" );
            XmlBufferAppendString( buf, nodeName );

            if ( nodeptr->firstAttr != NULL )
            {
                XmlBufferAppendString( buf, " " );
                XmlPrintDomTreeRecursive( nodeptr->firstAttr, buf );
            }

            child = XmlNodeGetFirstChild( nodeptr );
            if ( ( child != NULL )
                    && ( XmlNodeGetNodeType( child ) ==
                         E_ELEMENT_NODE ) )
            {
                XmlBufferAppendString( buf, ">\n" );
            }
            else
            {
                XmlBufferAppendString( buf, ">" );
            }

            //  output the children
            XmlPrintDomTreeRecursive( XmlNodeGetFirstChild
                                        ( nodeptr ), buf );

            // Done with children.  Output the end tag.
            XmlBufferAppendString( buf, "</" );
            XmlBufferAppendString( buf, nodeName );

            sibling = XmlNodeGetNextSibling( nodeptr );
            if ( sibling != NULL
                    && XmlNodeGetNodeType( sibling ) == E_TEXT_NODE )
            {
                XmlBufferAppendString( buf, ">" );
            }
            else
            {
                XmlBufferAppendString( buf, ">\n" );
            }
            XmlPrintDomTreeRecursive( XmlNodeGetNextSibling
                                        ( nodeptr ), buf );
            break;

        default:
            break;
        }
    }
}

/*================================================================
*   XmlPrintDomTree
*       Print a DOM tree.
*       Element, and Attribute nodes are handled differently.
*       We don't want to print the Element and Attribute nodes' sibling.
*       External function.
*
*=================================================================*/
void
XmlPrintDomTree( IN XmlNode *   nodeptr,
                   IN XmlBuffer * buf )
{
    char *nodeName = NULL;
    char *nodeValue = NULL;
    XmlNode *  child = NULL;

    if ( ( nodeptr == NULL ) || ( buf == NULL ) )
    {
        return;
    }

    nodeName = ( char * )XmlNodeGetNodeName( nodeptr )?:"";
    nodeValue = XmlNodeGetNodeValue( nodeptr );

    switch ( XmlNodeGetNodeType( nodeptr ) )
    {

    case E_TEXT_NODE:
    case E_CDATA_SECTION_NODE:
    case E_PROCESSING_INSTRUCTION_NODE:
    case E_DOCUMENT_NODE:
        XmlPrintDomTreeRecursive( nodeptr, buf );
        break;

    case E_ATTRIBUTE_NODE:
        XmlBufferAppendString( buf, nodeName );
        XmlBufferAppendString( buf, "=\"" );
        XmlBufferAppendString( buf, nodeValue );
        XmlBufferAppendString( buf, "\"" );
        break;

    case E_ELEMENT_NODE:
        XmlBufferAppendString( buf, "<" );
        XmlBufferAppendString( buf, nodeName );

        if ( nodeptr->firstAttr != NULL )
        {
            XmlBufferAppendString( buf, " " );
            XmlPrintDomTreeRecursive( nodeptr->firstAttr, buf );
        }

        child = XmlNodeGetFirstChild( nodeptr );
        if ( ( child != NULL )
                && ( XmlNodeGetNodeType( child ) == E_ELEMENT_NODE ) )
        {
            XmlBufferAppendString( buf, ">\n" );
        }
        else
        {
            XmlBufferAppendString( buf, ">" );
        }

        //  output the children
        XmlPrintDomTreeRecursive( XmlNodeGetFirstChild( nodeptr ),
                                    buf );

        // Done with children.  Output the end tag.
        XmlBufferAppendString( buf, "</" );
        XmlBufferAppendString( buf, nodeName );
        XmlBufferAppendString( buf, ">\n" );
        break;

    default:
        break;
    }
}

/*================================================================
*	XmlPrintDomTreeIdent
*       It is a recursive function to print all the node in a tree.
*       Internal to parser only.
*
*=================================================================*/
void
XmlPrintDomTreeIdent( IN XmlNode *   nodeptr,
                        IN XmlBuffer * buf )
{
    char *nodeName = NULL;
    char *nodeValue = NULL;
    XmlNode *  child = NULL;
    XmlNode *  sibling = NULL;
    int i;
    static int curDeepth = 0;

    if ( nodeptr != NULL )
    {
        nodeName = ( char * )XmlNodeGetNodeName( nodeptr )?:"";
        nodeValue = XmlNodeGetNodeValue( nodeptr );

        switch ( XmlNodeGetNodeType( nodeptr ) )
        {

        case E_TEXT_NODE:
            XmlCopyWithEscape( buf, nodeValue );
            break;

        case E_CDATA_SECTION_NODE:
            XmlBufferAppendString( buf, nodeValue );
            break;

        case E_PROCESSING_INSTRUCTION_NODE:
            XmlBufferAppendString( buf, "<?" );
            XmlBufferAppendString( buf, nodeName );
            XmlBufferAppendString( buf, " " );
            XmlBufferAppendString( buf, nodeValue );
            XmlBufferAppendString( buf, "?>\n" );
            break;

        case E_DOCUMENT_NODE:
            XmlPrintDomTreeIdent( XmlNodeGetFirstChild
                                    ( nodeptr ), buf );
            break;

        case E_ATTRIBUTE_NODE:
            XmlBufferAppendString( buf, nodeName );
            XmlBufferAppendString( buf, "=\"" );
            if ( nodeValue != NULL )
            {
                XmlBufferAppendString( buf, nodeValue );
            }
            XmlBufferAppendString( buf, "\"" );
            if ( nodeptr->nextSibling != NULL )
            {
                XmlBufferAppendString( buf, " " );
                XmlPrintDomTreeIdent( nodeptr->nextSibling, buf );
            }
            break;

        case E_ELEMENT_NODE:

            for (i=0;i<curDeepth;i++)
                XmlBufferAppendString( buf, "  " );

            XmlBufferAppendString( buf, "<" );
            XmlBufferAppendString( buf, nodeName );

            if ( nodeptr->firstAttr != NULL )
            {
                XmlBufferAppendString( buf, " " );
                XmlPrintDomTreeIdent( nodeptr->firstAttr, buf );
            }

            child = XmlNodeGetFirstChild( nodeptr );
            if ( ( child != NULL )
                    && ( XmlNodeGetNodeType( child ) ==
                         E_ELEMENT_NODE ) )
            {

                XmlBufferAppendString( buf, ">\n" );

                curDeepth++;

            }
            else
            {
                XmlBufferAppendString( buf, ">" );
            }

            //  output the children
            XmlPrintDomTreeIdent( XmlNodeGetFirstChild
                                    ( nodeptr ), buf );

            if (( child != NULL ) &&
                    XmlNodeGetNodeType( child ) == E_ELEMENT_NODE)
            {
                curDeepth--;

                for (i=0;i<curDeepth;i++)
                    XmlBufferAppendString( buf, "  " );
            }

            // Done with children.  Output the end tag.
            XmlBufferAppendString( buf, "</" );
            XmlBufferAppendString( buf, nodeName );

            sibling = XmlNodeGetNextSibling( nodeptr );
            if ( sibling != NULL
                    && XmlNodeGetNodeType( sibling ) == E_TEXT_NODE )
            {
                XmlBufferAppendString( buf, ">" );
            }
            else
            {

                XmlBufferAppendString( buf, ">\n" );

            }
            XmlPrintDomTreeIdent( XmlNodeGetNextSibling
                                    ( nodeptr ), buf );
            break;

        default:
            break;
        }
    }

}

/*================================================================
*   XmlDomTreetoString
*       Converts a DOM tree into a text string
*       Element, and Attribute nodes are handled differently.
*       We don't want to print the Element and Attribute nodes' sibling.
*       External function.
*
*=================================================================*/
void
XmlDomTreetoString( IN XmlNode *   nodeptr,
                      IN XmlBuffer * buf )
{
    char *nodeName = NULL;
    char *nodeValue = NULL;
    XmlNode *  child = NULL;

    if ( ( nodeptr == NULL ) || ( buf == NULL ) )
    {
        return;
    }

    nodeName = ( char * )XmlNodeGetNodeName( nodeptr )?:"";
    nodeValue = XmlNodeGetNodeValue( nodeptr );

    switch ( XmlNodeGetNodeType( nodeptr ) )
    {

    case E_TEXT_NODE:
    case E_CDATA_SECTION_NODE:
    case E_PROCESSING_INSTRUCTION_NODE:
    case E_DOCUMENT_NODE:
        XmlPrintDomTreeRecursive( nodeptr, buf );
        break;

    case E_ATTRIBUTE_NODE:
        XmlBufferAppendString( buf, nodeName );
        XmlBufferAppendString( buf, "=\"" );
        XmlBufferAppendString( buf, nodeValue );
        XmlBufferAppendString( buf, "\"" );
        break;

    case E_ELEMENT_NODE:
        XmlBufferAppendString( buf, "<" );
        XmlBufferAppendString( buf, nodeName );

        if ( nodeptr->firstAttr != NULL )
        {
            XmlBufferAppendString( buf, " " );
            XmlPrintDomTreeRecursive( nodeptr->firstAttr, buf );
        }

        child = XmlNodeGetFirstChild( nodeptr );
        if ( ( child != NULL )
                && ( XmlNodeGetNodeType( child ) == E_ELEMENT_NODE ) )
        {
            XmlBufferAppendString( buf, ">" );
        }
        else
        {
            XmlBufferAppendString( buf, ">" );
        }

        //  output the children
        XmlPrintDomTreeRecursive( XmlNodeGetFirstChild( nodeptr ),
                                    buf );

        // Done with children.  Output the end tag.
        XmlBufferAppendString( buf, "</" );
        XmlBufferAppendString( buf, nodeName );
        XmlBufferAppendString( buf, ">" );
        break;

    default:
        break;
    }
}

/*================================================================
*   XmlLoadDocumentEx
*       Parses the given file, and returns the DOM tree from it.
*       External function.
*
*=================================================================*/
int
XmlLoadDocumentEx( IN char *xmlFile,
                     XmlDocument ** doc )
{

    if ( ( xmlFile == NULL ) || ( doc == NULL ) )
    {
        return XML_INVALID_PARAMETER;
    }

    return XmlParserLoadDocument( doc, xmlFile, TRUE );
}

/*================================================================
*   XmlLoadDocument
*       Parses the given file, and returns the DOM tree from it.
*       External function.
*
*=================================================================*/
XmlDocument *
XmlLoadDocument( IN char *xmlFile )
{

    XmlDocument *doc = NULL;

    XmlLoadDocumentEx( xmlFile, &doc );
    return doc;
}

/*================================================================
*   XmlPrintNode
*       Print DOM tree under node. Puts lots of white spaces
*       External function.
*
*=================================================================*/
DOMString
XmlPrintNode( IN XmlNode *   node )
{

    XmlBuffer memBuf;
    XmlBuffer *buf = &memBuf;

    if ( node == NULL )
    {
        return NULL;
    }

    XmlBufferInit( buf );
    XmlPrintDomTree( node, buf );
    return buf->buf;

}

/*================================================================
*   XmlPrintNode
*       converts DOM tree under node to text string
*       External function.
*
*=================================================================*/
DOMString
XmlNodeToString( IN XmlNode *   node )
{

    XmlBuffer memBuf;
    XmlBuffer *buf = &memBuf;

    if ( node == NULL )
    {
        return NULL;
    }

    XmlBufferInit( buf );
    XmlDomTreetoString( node, buf );
    return buf->buf;

}

/*================================================================
*   XmlParseBufferEx
*       Parse xml file stored in buffer.
*       External function.
*
*=================================================================*/
int
XmlParseBufferEx( IN char *buffer,
                    XmlDocument ** retDoc )
{

    if ( ( buffer == NULL ) || ( retDoc == NULL ) )
    {
        return XML_INVALID_PARAMETER;
    }

    if ( strlen( buffer ) == 0 )
    {
        return XML_INVALID_PARAMETER;
    }

    return XmlParserLoadDocument( retDoc, buffer, FALSE );
}

/*================================================================
*   XmlParseBuffer
*       Parse xml file stored in buffer.
*       External function.
*
*=================================================================*/
XmlDocument *
XmlParseBuffer( IN char *buffer )
{
    XmlDocument *doc = NULL;

    XmlParseBufferEx( buffer, &doc );
    return doc;
}

/*================================================================
*   XmlCloneDOMString
*       Clones a DOM String.
*       External function.
*
*=================================================================*/
DOMString
XmlCloneDOMString( IN const DOMString src )
{
    if ( src == NULL )
    {
        return NULL;
    }

    return ( strdup( src ) );
}

/*================================================================
*   XmlFreeDOMString
*       Frees a DOM String.
*       External function.
*
*=================================================================*/
void
XmlFreeDOMString( IN DOMString buf )
{
    if ( buf != NULL )
    {
        free( buf );
    }
}
